package edu.kit.csl.pisa.datatypes;

/*
This file is part of the PISA Alignment Tool.

Copyright (C) 2013
Karlsruhe Institute of Technology
Cognitive Systems Lab (CSL)
Felix Stahlberg

PISA is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

PISA is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PISA. If not, see <http://www.gnu.org/licenses/>.
*/

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
 * A instance of this class represents a source language - target language
 * sentence pair and a set of possible alignments between them. This is the
 * basic data entity used for training the models.
 * Note that this class implements the GoF design pattern flyweight together
 * with {@link SentenceAlignment}, where the intrinsic state includes the
 * alignment, and the extrinsic state is composed from the source and
 * target language sentences. The fabric method for creating flyweight
 * instances (i.e. SentenceAlignment instances) is {@link #iterator()}.
 * 
 * @see SentenceAlignment
 */
public class SentencePair implements Iterable<SentenceAlignment> {

	/*
	 * Source and target language sentences
	 */
	private Sentence srcSentence, trgtSentence;
	
	/*
	 * Currently best alignment
	 */
	private Alignment viterbiAlignment;
	private double viterbiScore;
	private final String id;
	
	/*
	 * Set of possible alignments (including the bestAlignment) which can be
	 * used as start points for MaximationStrategy instances. This list is
	 * ordered by the time added, not the alignment score. The alignment
	 * score is only used to identify the currently best alignment. 
	 */
	private List<Alignment> alignments;
	private List<Double> scores;
	
	/**
	 * Sole constructor.
	 * 
	 * @param srcSentence the source sentence
	 * @param trgtSentence the target sentence
	 * @param id sentence pair id
	 * @see Sentence
	 */
	public SentencePair(Sentence srcSentence, Sentence trgtSentence, 
			String id) {
		this.srcSentence = srcSentence;
		this.trgtSentence = trgtSentence;
		viterbiScore = Double.NEGATIVE_INFINITY;
		alignments = new LinkedList<Alignment>();
		scores = new LinkedList<Double>();
		this.id = id;
	}
	
	/**
	 * Get the sentence pair id.
	 * 
	 * @return the sentence pair id
	 */
	public String getId() {
		return id;
	}
	
	/**
	 * Get the source sentence.
	 * 
	 * @return the source sentence
	 */
	public Sentence getSourceSentence() {
		return srcSentence;
	}

	/**
	 * Get the target sentence.
	 * 
	 * @return the target sentence
	 */
	public Sentence getTargetSentence() {
		return trgtSentence;
	}
	
	/**
	 * Remove all alignments.
	 * 
	 * @see #addAlignment(Alignment)
	 */
	public void removeAllAlignments() {
		viterbiAlignment = null;
		viterbiScore = Double.NEGATIVE_INFINITY;
		alignments.clear();
		scores.clear();
	}
	
	/**
	 * Get a list of alignments added so far. Note that this list remains
	 * unmodified, even if {@link #removeAllAlignments()} is called. This
	 * makes it possible to remove all alignments, but then iterate through 
	 * them afterwards to update the alignment scores.
	 * 
	 * @return all alignments for this sentence pair
	 */
	public List<Alignment> getAlignments() {
		return alignments;
	}
	
	/**
	 * Add an alignment to the set of the possible alignments. If its score is
	 * higher than the best added alignment score so far, we update the best
	 * alignment reference.
	 * 
	 * @param alignment new possible alignment between src and trgt sentence 
	 */
	public void addAlignment(Alignment alignment, double score) {
		alignments.add(alignment);
		scores.add(score);
		if (score > viterbiScore) {
			viterbiScore = score;
			viterbiAlignment = alignment;
		}
	}
	
	/**
	 * Get the number of alignments added so far.
	 * 
	 * @return the number of alignments for this sentence pair
	 */
	public int countAlignments() {
		return alignments.size();
	}
	
	/**
	 * Get the best alignment so far.
	 * 
	 * @return the viterbiAlignment reference (null if there are no alignments)
	 */
	public SentenceAlignment getViterbiAlignment() {
		return viterbiAlignment == null ? null :
			new SentenceAlignment(viterbiAlignment, srcSentence, trgtSentence,
					viterbiScore);
	}
	
	/**
	 * Get the alignment score of the viterbi alignment. If there are no
	 * alignments, return {@link Double.NEGATIVE_INFINITY}.
	 * 
	 * @return the score of the current viterbi alignment or -infinity
	 */
	public double getViterbiScore() {
		return viterbiScore;
	}

	/**
	 * Returns an iterator for this sentence pair's alignments. This is also
	 * a fabric method (GoF) for {@link SentenceAlignment} instances.
	 * 
	 * @see Iterable
	 * @see SentenceAlignment
	 */
	@Override
	public Iterator<SentenceAlignment> iterator() {
        Iterator<SentenceAlignment> it = new Iterator<SentenceAlignment>() {
            private Iterator<Alignment> alignIterator = alignments.iterator();
            private Iterator<Double> scoreIterator = scores.iterator();
            
            @Override
            public boolean hasNext() {
            	return alignIterator.hasNext();
            }

            @Override
            public SentenceAlignment next() {
                return new SentenceAlignment(alignIterator.next(),
                		srcSentence, trgtSentence, scoreIterator.next());
            }

            @Override
            public void remove() {
                alignIterator.remove();
                scoreIterator.remove();
            }
        };
		return it;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return 
			srcSentence.toString(
					Dictionary.getSingleton(Dictionary.SRC_DICT)) 
			+ "\n" 
			+ trgtSentence.toString(
					Dictionary.getSingleton(Dictionary.TRGT_DICT));
	}
}
